package ccm.component.image;

import ccm.common.Buffer;
import ccm.common.Crc;
import ccm.common.ReceiveDataEvent;
import ccm.common.ReceiveDataListener;

import java.awt.image.BufferedImage;
import java.util.ArrayList;

public class ImageDecoder implements ReceiveDataListener
{
    /**
     * 接收缓冲
     */
    protected final byte[] receiveBuf;
    /**
     * 接收到图片监听器
     */
    protected final ArrayList<ReceiveImageListener> receiveImageListener;
    /**
     * 接收缓冲起始
     */
    protected int receiveBufStart;
    /**
     * 接收状态
     */
    protected ReceiveState receiveState;
    /**
     * 当前接收横坐标
     */
    protected int receivingI;
    /**
     * 当前接收纵坐标
     */
    protected int receivingJ;
    /**
     * 接收图片缓冲
     */
    protected BufferedImage image;
    protected int imageWidth;
    protected int imageHeight;
    protected int imageType;


    public ImageDecoder()
    {
        receiveBuf=new byte[(4+2+2+1+2)];
        receiveImageListener=new ArrayList<>();
        clear();
    }

    public void clear()
    {
        receiveBufStart=0;
        receiveState=ReceiveState.RECEIVING_HEAD;
        imageWidth=0;
        imageHeight=0;
        imageType=0;
        receivingI=0;
        receivingJ=0;
    }

    /**
     * 接收到数据事件
     */
    @Override
    synchronized public void receiveData(ReceiveDataEvent event)
    {
        System.out.println("ImageDecoder receiveData");
        for(byte datum: event.getData())
        {
            final int headLen=(4+2+2+1+2);
            final int tailLen=(4);
            switch(receiveState)
            {
                case RECEIVING_HEAD:
                    receiveBuf[receiveBufStart]=datum;
                    receiveBufStart=(receiveBufStart+1)%receiveBuf.length;
                    if(Buffer.getUint8(receiveBuf,receiveBufStart-headLen)==0X00&&Buffer.getUint8(receiveBuf,receiveBufStart-headLen+1)==0xFF&&Buffer.getUint8(receiveBuf,receiveBufStart-headLen+2)==0x01&&Buffer.getUint8(receiveBuf,receiveBufStart-headLen+3)==0x01)
                    {
                        if(Buffer.getUint16(receiveBuf,receiveBufStart-2)==Crc.crc16Modbus(receiveBuf,receiveBufStart-headLen,headLen-2))
                        {
                            imageType=Buffer.getUint8(receiveBuf,receiveBufStart+4);
                            imageWidth=Buffer.getUint16(receiveBuf,receiveBufStart+5);
                            imageHeight=Buffer.getUint16(receiveBuf,receiveBufStart+7);
                            receiveState=ReceiveState.RECEIVING_IMAGE;
                            receiveBufStart=0;
                            receivingI=0;
                            receivingJ=0;
                            image=new BufferedImage(imageWidth,imageHeight,BufferedImage.TYPE_INT_RGB);
                            System.out.println("ImageDecoder receive head "+imageWidth+","+imageHeight+","+imageType);
                        }
                        else
                        {
                            System.out.println(Buffer.getUint16(receiveBuf,receiveBufStart-2));
                            System.out.println("ImageDecoder receive head crc failed");
                        }
                    }
                    break;
                case RECEIVING_IMAGE:
                    if(image!=null)
                    {
                        if(imageType==0)
                        {
                            int dat=Byte.toUnsignedInt(datum);
                            image.setRGB(receivingI,receivingJ,(((dat)&0XFF)<<16)|(((dat)&0XFF)<<8)|(((dat)&0XFF)));
                            ++receivingI;
                        }
                        else if(imageType==5)
                        {
                            receiveBuf[receiveBufStart]=datum;
                            receiveBufStart=(receiveBufStart+1)%receiveBuf.length;
                            if(receiveBufStart==4)
                            {
                                image.setRGB(receivingI,receivingJ,(int)Buffer.getUint32(receiveBuf,0));
                                receiveBufStart=0;
                                ++receivingI;
                            }
                        }
                        else
                        {++receivingI;}
                        if(receivingI>=imageWidth)
                        {
                            receivingI=0;
                            ++receivingJ;
                        }
                        if(receivingJ>=imageHeight)
                        {
                            receiveState=ReceiveState.RECEIVING_TAIL;
                        }
                    }
                    else
                    {receiveState=ReceiveState.RECEIVING_HEAD;}
                    break;
                case RECEIVING_TAIL:
                    receiveBuf[receiveBufStart]=datum;
                    receiveBufStart=(receiveBufStart+1)%receiveBuf.length;
                    if(Buffer.getUint8(receiveBuf,receiveBufStart-tailLen)==0X01&&Buffer.getUint8(receiveBuf,receiveBufStart-tailLen+1)==0x01&&Buffer.getUint8(receiveBuf,receiveBufStart-tailLen+2)==0xFF&&Buffer.getUint8(receiveBuf,receiveBufStart-tailLen+3)==0x00)
                    {
                        receiveImageListener.forEach(r->
                        {
                            try
                            {
                                r.receiveImage(new ReceiveImageEvent(this,image));
                            }catch(Exception ex)
                            {
                                ex.printStackTrace();
                            }

                        });
                        receiveState=ReceiveState.RECEIVING_HEAD;
                    }
                    break;
            }

        }
    }

    /**
     * 添加图片接收监听器
     *
     * @param listener 监听器
     * @throws NullPointerException 监听器为null时抛出此异常
     */
    public void addReceiveImageListener(ReceiveImageListener listener) throws NullPointerException
    {
        if(listener==null){throw new NullPointerException();}
        if(!receiveImageListener.contains(listener)){receiveImageListener.add(listener);}
    }

    protected enum ReceiveState
    {
        RECEIVING_HEAD,RECEIVING_IMAGE,RECEIVING_TAIL,
    }
}
